Skip to content

U5: Enable MSI auto calibration and compute frequencies #4313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

snakehand
Copy link
Contributor

This adresses #4312

@Dirbaio
Copy link
Member

Dirbaio commented Jun 22, 2025

Thanks for the PR!

From looking at the RM, it seems to me MSI PLL is only usable when LSE is exactly 32768hz, and therefore the MSI freq will always be exactly what's specified by MSIRANGE, so we can always use msirange_to_hertz and calculate_calibrated_msi_frequency is not actually needed. Am I missing something?

screenshot-2025-06-22_23-30-42

@snakehand
Copy link
Contributor Author

We have tested this in the lab with a 32000 Hz source, over wide temperature ranges, and have found that the "PLL" works over a wider range of temperatures. It is not described as a PLL in the documentation, but as "auto-calibration feature" , and I believe it is implemented by a couple of counters that are in a control loop with the calibration function. The calibration function has a wide offset range (+/- several % ) , and it is reasonable to assume that a similar range of input frequencies must work for the reference.

@Dirbaio
Copy link
Member

Dirbaio commented Jun 30, 2025

"when a 32.768khz external oscillator is present" strongly implies other frequencies are not supported.

Also, the section for the LSE also says it must be a 32768hz crystal. Other frequencies are only allowed in bypass mode. And "when a 32.768khz external oscillator is present" also implies it must be an oscillator, not bypass mode.

STM32CubeMX only allows 32768hz if LSE is a crystal. It doesn't seem to complain if you enable MSI PLL and set LSE to another frequency, but it doesn't change the MSI frequency based on it like this PR does.

Do you have a source from ST stating this is supported? I'm a bit hesitant of adding support for running out of spec if it increases complexity.

@@ -131,7 +131,18 @@ pub(crate) unsafe fn init(config: Config) {
PWR.vosr().modify(|w| w.set_vos(config.voltage_range));
while !PWR.vosr().read().vosrdy() {}

let msis = config.msis.map(|range| {
let lse_calibration_freq = match config.ls.lse {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of automatically deciding to enable msipll, could you add it to the config so the user explicitly chooses to. It could be None, Some(Msik) or Some(Msis).

If ther'es no LSE or the freq is too off just panic, this way the user notices if they do something wrong instead of it silently doing no calibration.

/// Based on STM32U5 datasheet table for LSE = 32.768 kHz
fn get_msi_calibration_fraction(range: Msirange) -> MsiFraction {
match range {
Msirange::RANGE_48MHZ => MsiFraction::new(1465, 1), // Range 0: 48.005 MHz
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for each "group" of 4 the numerator is the same and the denominator is 1, 2, 3, 4. Maybe this can help compactify the table?

Msirange::RANGE_48MHZ     => MsiFraction::new(1465, 1), // Range 0: 48.005 MHz
Msirange::RANGE_24MHZ     => MsiFraction::new(1465, 2), // Range 1: 24.003 MHz
Msirange::RANGE_16MHZ     => MsiFraction::new(1465, 3), // Range 2: 16.002 MHz
Msirange::RANGE_12MHZ     => MsiFraction::new(1465, 4), // Range 3: 12.001 MHz
Msirange::RANGE_4MHZ      => MsiFraction::new(122, 1),  // Range 4: 3.998 MHz
Msirange::RANGE_2MHZ      => MsiFraction::new(122, 2),  // Range 5: 1.999 MHz
Msirange::RANGE_1_33MHZ   => MsiFraction::new(122, 3),  // Range 6: 1.333 MHz
Msirange::RANGE_1MHZ      => MsiFraction::new(122, 4),  // Range 7: 0.999 MHz
Msirange::RANGE_3_072MHZ  => MsiFraction::new(94, 1),   // Range 8: 3.08 MHz
Msirange::RANGE_1_536MHZ  => MsiFraction::new(94, 1),   // Range 9: 1.54 MHz
Msirange::RANGE_1_024MHZ  => MsiFraction::new(94, 3),   // Range 10: 1.027 MHz
Msirange::RANGE_768KHZ    => MsiFraction::new(94, 4),   // Range 11: 0.77 MHz
Msirange::RANGE_400KHZ    => MsiFraction::new(12, 1),   // Range 12: 393 kHz
Msirange::RANGE_200KHZ    => MsiFraction::new(12, 2),   // Range 13: 196.6 kHz
Msirange::RANGE_133KHZ    => MsiFraction::new(12, 3),   // Range 14: 131 kHz
Msirange::RANGE_100KHZ    => MsiFraction::new(12, 4),   // Range 15: 98.3 kHz

if let Some(freq) = lse_calibration_freq {
let msik_freq = calculate_calibrated_msi_frequency(range, freq);
if config.msis == config.msik {
// If MSIS and MSIK are the same range both will be auto calibrated to the same frequency
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they don't need to be the exact same, it's enough if they're in the same RC (group of 4)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants